package com.vhall.component.service.rebroadcast.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.vhall.component.dao.rebroadcast.RebroadCastMappers;
import com.vhall.component.entity.rebroadcast.RebroadCastEntity;
import com.vhall.component.entity.rebroadcast.vo.ListReqVo;
import com.vhall.component.entity.rebroadcast.vo.PreviewReqVo;
import com.vhall.component.entity.rebroadcast.vo.PreviewRspVo;
import com.vhall.component.entity.rebroadcast.vo.StartReqVo;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.PaasCommonConstant;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.rebroadcast.RebroadCastService;
import com.vhall.component.service.room.RoomInnerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * @author xianglei
 * @date 2021/6/16 11:35
 */
@Slf4j
@Service
public class RebroadCastServiceImpl implements RebroadCastService {

    @Autowired
    private RoomInnerService roomInnerService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RebroadCastMappers rebroadCastMappers;

    @Autowired
    private PaasService paasService;

    @Value("${paas.static-domain}")
    private String documentUrl;

    /**
     * 开始转播
     *
     * @param
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean start(StartReqVo reqVO) {
        if (reqVO.getRoomId().equals(reqVO.getSourceRoomId())) {
            throw new BusinessException(BizErrorCode.COMP_REBROADCAST_ORIGIN_SAME);
        }

        //原始房间 信息
        RoomsEntityDTO oldRoomInfo = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (Objects.isNull(oldRoomInfo)) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }

        // 被转发的房间信息
        RoomsEntityDTO targetRoom = roomInnerService.getRoomInfo(reqVO.getSourceRoomId());
        if (Objects.isNull(targetRoom)) {
            throw new BusinessException(BizErrorCode.BIZ_LIVE_CODE_INVALID);
        }

        if (targetRoom.getStatus() == 0) {
            throw new BusinessException(BizErrorCode.COMP_REBROADCAST_LIVE_NOT_START);
        }
        if (targetRoom.getStatus() == 2) {
            throw new BusinessException(BizErrorCode.COMP_REBROADCAST_LIVE_END);
        }

      /*  RebroadCastEntity isExistSource = getStartRebroadcastByRoomId(reqVO.getSourceRoomId());
        if (isExistSource != null) {
            throw new BusinessException(BizErrorCode.COMP_REBROADCAST_REPEATED);
        }*/

        RebroadCastEntity stream = setRebroadcast(reqVO.getRoomId(), reqVO.getSourceRoomId(), 1);
        if (stream == null) {
            throw new BusinessException(BizErrorCode.COMP_REBROADCAST_SET_FAIL);
        }
        Boolean pullStream = paasService.pullStream(reqVO.getRoomId(), reqVO.getSourceRoomId(), stream.getId(), "start");

        setChannel(oldRoomInfo, targetRoom);

        HashMap<String, Object> data = new HashMap<>(16);
        data.put("type", "live_broadcast_start");
        data.put("channel_id", targetRoom.getChannelId());

        paasService.sendMessage(oldRoomInfo.getChannelId(), JSON.toJSONString(data));

        //获取被转播房间的文档信息
        JSONObject watchInfo = paasService.getDocumentWatchInfo(targetRoom.getChannelId());
        if (watchInfo != null) {
            JSONArray list = (JSONArray) watchInfo.get("list");
            if (list.size() != 0) {
                JSONObject info = list.getJSONObject(0);
                Long uniqueId = System.currentTimeMillis();
                //创建消息
                HashMap<String, Object> commandToCreate = new HashMap<>(8);
                HashMap<String, Object> dataToCreate = new HashMap<>(8);
                dataToCreate.put("id", info.getString("cid"));
                dataToCreate.put("width", info.getInteger("width"));
                dataToCreate.put("height", info.getInteger("height"));
                dataToCreate.put("backgroundColor", info.getString("backgroundColor"));
                dataToCreate.put("type", info.getInteger("is_board") == 2 ? "board" : "document");
                commandToCreate.put("type", "create");
                commandToCreate.put("data", dataToCreate);
                commandToCreate.put("stamp", uniqueId);
                commandToCreate.put("event", "operate");
                commandToCreate.put("op", "create");
                commandToCreate.put("is_board", info.getInteger("is_board"));
                Map<String, Object> paramToCreate = getParam(info, uniqueId, 0, commandToCreate);

                log.info("1发给pass的消息体数据是:" + JSON.toJSONString(paramToCreate));

                paasService.sendMessage(oldRoomInfo.getChannelId(), JSON.toJSONString(paramToCreate), oldRoomInfo.getAccountId().toString(), "service_document", "pc_browser", 0);
                //选容器
                HashMap<String, Object> commandToChoose = new HashMap<>(16);
                HashMap<String, Object> dataToChoose = new HashMap<>(16);
                dataToChoose.put("id", info.getString("cid"));
                commandToChoose.put("type", "active");
                commandToChoose.put("data", dataToChoose);
                commandToChoose.put("stamp", uniqueId);
                commandToChoose.put("event", "operate");
                commandToChoose.put("op", "active");
                commandToChoose.put("is_board", info.getInteger("is_board"));
                Map<String, Object> paramToChoose = getParam(info, uniqueId, 0, commandToChoose);


                log.info("2发给pass的消息体数据是:" + JSON.toJSONString(paramToChoose));
                paasService.sendMessage(oldRoomInfo.getChannelId(), JSON.toJSONString(paramToChoose), oldRoomInfo.getAccountId().toString(), "service_document", "pc_browser", 0);

                //翻页
                HashMap<String, Object> commandToTurnPage = new HashMap<>(8);
                HashMap<String, Object> dataToTurnPage = new HashMap<>(4);
                HashMap<String, Object> infoToTurnPage = new HashMap<>(8);
                HashMap<String, Object> paramsToTurnPage = new HashMap<>(8);

                infoToTurnPage.put("slidesTotal", info.getInteger("page"));
                infoToTurnPage.put("slideIndex", info.getInteger("show_page"));
                infoToTurnPage.put("pageHash", info.getString("hash") + "/" + info.getString("show_page"));
                infoToTurnPage.put("hash", info.getString("hash"));
                infoToTurnPage.put("stepsAll", 0);
                dataToTurnPage.put("stepsTotal", 0);
                dataToTurnPage.put("docType", info.getInteger("doc_type") == 2 ? "jpg" : "ppt");

                paramsToTurnPage.put("slideIndex", info.getInteger("show_page"));
                paramsToTurnPage.put("slidesTotal", info.getInteger("page"));
                paramsToTurnPage.put("stepsTotal", 0);
                paramsToTurnPage.put("stepIndex", info.getString("show_step"));

                commandToTurnPage.put("type", "create");
                commandToTurnPage.put("data", dataToChoose);
                commandToTurnPage.put("stamp", uniqueId);
                commandToTurnPage.put("event", "operate");
                commandToTurnPage.put("op", "create");
                commandToTurnPage.put("is_board", info.getInteger("is_board"));
                Map<String, Object> paramToTurnPage = getParam(info, uniqueId, 1, commandToTurnPage);

                log.info("3发给pass的消息体数据是:" + JSON.toJSONString(paramToTurnPage));
                paasService.sendMessage(oldRoomInfo.getChannelId(), JSON.toJSONString(paramToTurnPage), oldRoomInfo.getAccountId().toString(), "service_document", "pc_browser", 0);

                //开关
                HashMap<String, Object> commandToSwitch = new HashMap<>(8);
                HashMap<String, Object> dataToSwitch = new HashMap<>(4);
                dataToSwitch.put("id", info.getString("cid"));
                commandToSwitch.put("type", info.getInteger("switch_status") == 1 ? "switchon" : "switchoff");
                commandToSwitch.put("data", dataToChoose);
                commandToSwitch.put("stamp", uniqueId);
                commandToSwitch.put("event", "operate");
                commandToSwitch.put("op", info.getInteger("switch_status") == 1 ? "switchon" : "switchoff");
                commandToSwitch.put("is_board", info.getInteger("is_board"));
                Map<String, Object> paramToSwitch = getParam(info, uniqueId, 0, commandToChoose);

                log.info("4发给pass的消息体数据是:" + JSON.toJSONString(paramToSwitch));

                paasService.sendMessage(oldRoomInfo.getChannelId(), JSON.toJSONString(paramToSwitch), oldRoomInfo.getAccountId().toString(), "service_document", "pc_browser", 0);

            }
        }
        return true;
    }

    /**
     * 开始转播中的param参数
     *
     * @param
     * @return
     */
    private Map<String, Object> getParam(JSONObject info, Long uniqueId, Integer flipOver, HashMap<String, Object> command) {
        HashMap<String, Object> param = new HashMap<>(16);
        param.put("hash", info.getString("hash"));
        param.put("uniqueID", uniqueId);
        param.put("currentPage", info.getInteger("show_page"));
        param.put("page", info.getInteger("page"));
        param.put("currentStep", info.getString("show_step"));
        param.put("step", 0);
        param.put("cw", info.getString("cw"));
        param.put("ch", info.getString("ch"));
        param.put("width", info.getInteger("width"));
        param.put("height", info.getInteger("height"));
        param.put("cid", info.getString("cid"));
        param.put("ext", info.getString("ext"));
        param.put("version", "1.1.0");
        param.put("doc_type", info.getString("doc_type"));
        param.put("docId", info.getString("docId"));
        param.put("pf", "js");
        param.put("flipOver", flipOver);
        param.put("command", command);
        return param;
    }

    private RebroadCastEntity getStartRebroadcastByRoomId(String roomId) {
        RebroadCastEntity isExistSource;
        String sourceRoomIdValue = (String) redisTemplate.opsForValue().get(RedisKey.REBROADCAST_START_INFO + roomId);
        if (sourceRoomIdValue == null) {
            isExistSource = rebroadCastMappers.selectOne(Wrappers.<RebroadCastEntity>lambdaQuery()
                    .eq(RebroadCastEntity::getRoomId, roomId)
                    .eq(RebroadCastEntity::getStatus, 1)
                    .last("limit 1"));
            if (isExistSource != null) {
                redisTemplate.opsForValue().set(RedisKey.REBROADCAST_START_INFO + roomId, JsonUtil.toJsonString(isExistSource), 20, TimeUnit.SECONDS);
            }
        } else {
            isExistSource = JSONObject.parseObject(sourceRoomIdValue, RebroadCastEntity.class);
        }
        return isExistSource;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public JSONObject stop(StartReqVo reqVO) {
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVO.getRoomId());
        RoomsEntityDTO sourceInfo = roomInnerService.getRoomInfo(reqVO.getSourceRoomId());
        if (roomInfo == null || sourceInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        if (reqVO.getRoomId().equals(reqVO.getSourceRoomId())) {
            throw new BusinessException(BizErrorCode.COMP_REBROADCAST_ORIGIN_SAME);
        }
        RebroadCastEntity stream = setRebroadcast(reqVO.getRoomId(), reqVO.getSourceRoomId(), 0);
        if (stream == null || stream.getId() == null) {
            throw new BusinessException(BizErrorCode.COMP_REBROADCAST_SET_FAIL);
        }
        paasService.pullStream(reqVO.getRoomId(), reqVO.getSourceRoomId(), stream.getId(), "stop");
        setChannel(roomInfo, sourceInfo);
        HashMap<String, Object> data = new HashMap<>(4);
        data.put("type", "live_broadcast_stop");
        data.put("channel_id", sourceInfo.getChannelId());

        return paasService.sendMessage(roomInfo.getChannelId(), JSON.toJSONString(data));
    }

    /**
     * 转播列表
     *
     * @param
     * @return
     */
    @Override
    public Map<String, Object> list(ListReqVo reqVo) {
        RebroadCastEntity rebroadcast = getStartRebroadcastByRoomId(reqVo.getRoomId());
        String sourceRoomId = null;
        if (rebroadcast != null && rebroadcast.getSourceRoomId() != null) {
            sourceRoomId = rebroadcast.getSourceRoomId();
        }
        reqVo.setSourceRoomId(sourceRoomId);
        return roomInnerService.getRoomsEntityByRebroadcastList(reqVo);
    }

    /**
     * 转播预览
     *
     * @param
     * @return
     */
    @Override
    public PreviewRspVo preview(PreviewReqVo reqVo) {
        RebroadCastEntity isExist = getStartRebroadcastByRoomId(reqVo.getSourceRoomId());

        if (isExist != null) {
            throw new BusinessException(BizErrorCode.COMP_REBROADCAST_REPEATED);
        }

        RoomsEntityDTO sourceInfo = roomInnerService.getRoomInfo(reqVo.getSourceRoomId());
        JSONObject watchInfo = null;
        if (sourceInfo != null) {
            watchInfo = paasService.getDocumentWatchInfo(sourceInfo.getChannelId());
        }

        String url = null;
        if (watchInfo != null) {
            JSONArray list = (JSONArray) watchInfo.get("list");
            if (list.size() != 0) {
                JSONObject info = list.getJSONObject(0);
                if (info.getInteger("switch_status") != null && info.getInteger("switch_status") == 1) {
                    url = documentUrl + "/document/" + info.getString("hash") + "/" + info.getInteger("show_page") + 1 + ".jpg";
                }
            }
        }
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(reqVo.getSourceRoomId());

        if (roomInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        JSONObject result = null;
        if (StringUtils.isNotBlank(reqVo.getThirdPartyUserId())) {
            TreeMap<String, Object> param = new TreeMap<>();
            param.put(PaasCommonConstant.THIRD_PARTY_USER_ID_KEY, reqVo.getThirdPartyUserId());
            param.put("publish_stream", roomInfo.getRoomId());
            param.put("chat", roomInfo.getChannelId());
            param.put("operate_document", roomInfo.getChannelId());
            param.put("kick_inav", roomInfo.getInavId());
            param.put("publish_inav_stream", roomInfo.getInavId());
            param.put("kick_inav_stream", roomInfo.getInavId());
            param.put("askfor_inav_publish", roomInfo.getInavId());
            param.put("audit_inav_publish", roomInfo.getInavId());
            param.put("publish_inav_another", roomInfo.getInavId());
            param.put("apply_inav_publish", roomInfo.getInavId());
            param.put("data_collect_manage", "all");
            param.put("data_collect_submit", "all");
            param.put("data_collect_view", "all");
            result = paasService.createAccessToken(param);
        }
        String accessToken = "";
        if (result != null) {
            accessToken = result.getString("access_token");
        }
        PreviewRspVo previewRspVo = new PreviewRspVo();
        BeanUtils.copyProperties(roomInfo, previewRspVo);
        previewRspVo.setThirdPartyUserId(reqVo.getThirdPartyUserId());
        previewRspVo.setPaasAccessToken(accessToken);
        if (result != null) {
            previewRspVo.setDocumentUrl(url);
        }
        return previewRspVo;
    }

    /**
     * 转播-设置房间频道
     *
     * @param olodRoom   原房间信息
     * @param targetRoom 目标房间
     */
    private void setChannel(RoomsEntityDTO olodRoom, RoomsEntityDTO targetRoom) {

        //这个查询的是  被转播房间 下面 所有 转播  这个 被转播房间的id

        // 比如 房间B开播
        //  房间 A 转播 房间B   房间C转播房间B    房间D转播房间B
        //     这个查询的roomsid 是  所有转播房间B 的 房间id

        List<RebroadCastEntity> rooms = rebroadCastMappers.selectList(Wrappers.<RebroadCastEntity>lambdaQuery()
                .eq(RebroadCastEntity::getSourceRoomId, targetRoom.getRoomId())
                .eq(RebroadCastEntity::getStatus, 1));
        List<RoomsEntityDTO> info = null;
        HashMap<String, Object> map = new HashMap<>(16);
        if (CollectionUtils.isNotEmpty(rooms)) {
            List<String> roomIdArr = rooms.stream().map(RebroadCastEntity::getRoomId).collect(Collectors.toList());
            String s = JSON.toJSONString(roomIdArr);
            info = roomInnerService.getRoomsInfoByRoomIds(s);
            if (info != null) {
                info.stream().forEach(r -> {
                    map.put(r.getChannelId(), r.getRoomId());
                });
                log.info("调用setTargetChannel的参数是: " + targetRoom.getChannelId(), "map是: " + JsonUtil.toJsonString(map));
                paasService.setTargetChannel(targetRoom.getChannelId(), JsonUtil.toJsonString(map));
            }
        }

        //  paasService.resetTargetChannel(targetRoom.getChannelId());

    }


    /**
     * 更新转播状态
     *
     * @param
     * @return
     */
    private RebroadCastEntity setRebroadcast(String roomId, String sourceId, Integer status) {
        status = status == null ? 0 : status;
        LocalDateTime now = LocalDateTime.now();
        RebroadCastEntity stream = rebroadCastMappers.selectOne(Wrappers.<RebroadCastEntity>lambdaQuery()
                .eq(RebroadCastEntity::getRoomId, roomId)
                .eq(RebroadCastEntity::getStatus, 1)
                .last("limit 1"));

        if (stream != null) {
            rebroadCastMappers.update(null, Wrappers.<RebroadCastEntity>lambdaUpdate()
                    .set(RebroadCastEntity::getStatus, 0)
                    .set(RebroadCastEntity::getEndTime, now)
                    .eq(RebroadCastEntity::getId, stream.getId()));

            redisTemplate.delete(RedisKey.REBROADCAST_INFO + roomId);
        }
        if (status != 0) {
            stream = new RebroadCastEntity();
            stream.setRoomId(roomId);
            stream.setSourceRoomId(sourceId);
            stream.setStatus(1);
            stream.setStartTime(now);
            rebroadCastMappers.insert(stream);
        }
        return stream;
    }
}
